﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using MongoDB.Bson;
using MongoDB.Driver;

using MongoDB.Driver.Builders;
using MongoDB.Driver.GridFS;
using MongoDB.Driver.Linq;

namespace Carbon.Web.Forms.MongoDbSupport
{
    public class MongoDbPersistanceService : IPersistanceService
    {
        public class MongoBlob
        {
            public ObjectId Id { get; set; }
            public Guid Key { get; set; }
            public byte[] Data { get; set; }
            public String Name { get; set; }
        }

        private MongoServer m_MongoServer = default(MongoServer);
        private bool m_MongoServer_calculated = false;
        public MongoServer MongoServer
        {
            get
            {
                if (!m_MongoServer_calculated)
                {
                    string connectionString = Configuration.Instance.ConnectionStringValue;
                    m_MongoServer = MongoServer.Create(connectionString);
                    m_MongoServer_calculated = true;
                }

                return m_MongoServer;
            }
        }

        
        private MongoDatabase m_Database = default(MongoDatabase);
        private bool m_Database_calculated = false;
        public MongoDatabase Database
        {
            get
            {
                if (!m_Database_calculated)
                {
                    m_Database = MongoServer.GetDatabase("__CarbonForms");
                    m_Database_calculated = true;
                }

                return m_Database;
            }
        }

        private bool m_Diposed = false;
        public void Dispose()
        {
            if (m_Diposed) throw new NotSupportedException("Already Disposed");

            if (m_MongoServer_calculated)
            {
                // Don't call this. Performance penalty 
                //m_MongoServer.Disconnect();
            }

            if (m_Database_calculated)
            {
            }

            m_Diposed = true;
        }


        public byte[] LoadBinaryData(Object id, Guid key)
        {
            return LoadFile(new FileReference(id, key, ""));
        }

        public Object StoreBinaryData(Guid key, byte[] data)
        {
            return StoreFile(key, "", data).Id;
        }

        public void UpdateBinaryData(Object id, Guid key, byte[] data)
        {
            MongoBlob entity = null;
            MongoCollection<MongoBlob> collection = Database.GetCollection<MongoBlob>("blobs");

            entity = GetEntity(id, key);
            entity.Data = data;
            collection.Save(entity);
        }

        public void FreeIdentifier(Object id, Guid key)
        {
            MongoCollection<MongoBlob> collection = Database.GetCollection<MongoBlob>("blobs");

            var query = Query.EQ("_id", ((ObjectId)id).ToString());
            var entity = GetEntity(id, key);
            if ((entity.Key == key) == false)  throw new InvalidOperationException("(entity.Key == key) == false");

            collection.Remove(query);
        }

        public FileReference StoreFile(Guid key, string name, byte[] content)
        {
            MongoBlob entity = new MongoBlob { Key = key, Name = name, Data = content };
            MongoCollection<MongoBlob> collection = Database.GetCollection<MongoBlob>("blobs");
            collection.Insert(entity);
            return new FileReference(entity.Id.ToString(), key, name);
        }

        private MongoBlob GetEntity(Object id, Guid key)
        {
            MongoBlob entity = null;
            MongoCollection<MongoBlob> collection = Database.GetCollection<MongoBlob>("blobs");
            var query = Query.And(Query.EQ("_id", ObjectId.Parse((string)id)), Query.EQ("Key", key));
            entity = collection.FindOne(query);
            if (entity == null)  throw new NullReferenceException("entity");
            if ((entity.Key == key) == false)  throw new InvalidOperationException("(entity.Key == key) == false");

            return entity;
        }

        public byte[] LoadFile(FileReference reference)
        {
            MongoBlob entity = null;
            entity = GetEntity(reference.Id, reference.Key);
            reference.Name = entity.Name;
            return entity.Data;
        }

        public void FreeFile(FileReference reference)
        {
            FreeIdentifier(reference.Id, reference.Key);
        }
    }
}
